package com.cennavi.mineplay.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.springframework.jdbc.core.JdbcTemplate;

import com.mercator.MercatorProjection;

public class GridHeatmapUtils {
	
	private static class Grid{
		
		double min_lng,min_lat,max_lng,max_lat;
		int count;

		public Grid(double min_lng, double min_lat, double max_lng,
				double max_lat,int count) {
			this.min_lng = min_lng;
			this.min_lat = min_lat;
			this.max_lng = max_lng;
			this.max_lat = max_lat;
			this.count = count;
		}
		
		private void scale(){
			double step_lng = (max_lng - min_lng) * 0.05;
			double step_lat = (max_lat - min_lat) * 0.05;
			this.min_lng += step_lng;
			this.max_lng -= step_lng;
			this.min_lat += step_lat;
			this.max_lat -= step_lat;
		}
		
		public byte[][] getGrid(){
			
			scale();
			
			byte[][] grid = new byte[5][];
			
			int min_lng_int = (int)(min_lng * 100000);
			int max_lng_int = (int)(max_lng * 100000);
			int min_lat_int = (int)(min_lat * 100000);
			int max_lat_int = (int)(max_lat * 100000);
			grid[0] = ABUtils.intToBytes2(min_lng_int);
			grid[1] = ABUtils.intToBytes2(min_lat_int);
			grid[2] = ABUtils.intToBytes2(max_lng_int);
			grid[3] = ABUtils.intToBytes2(max_lat_int);
			grid[4] = ABUtils.intToBytes2(this.count);
			
			return grid;
		}
		
		public byte[][] getGridhex(){
			
			//取中心
			double lng_center = (max_lng + min_lng)/2;
			double lat_center = (max_lat + min_lat)/2;
			//定义比例
			double ssize = 0.05;
			//逆时针取点
//			double lat_1 = lat_center + ((0.5 - ssize) * (max_lat - min_lat));
//			double lng_1 = lng_center;
//			
//			double lat_2 = lat_1 - ((lat_1 - lat_center) / 2);
//			double lng_2 = lng_center - ((lat_1 - lat_center) / 2 * Math.sqrt(3));
//			
//			double lat_3 = lat_center - ((lat_1 - lat_center) / 2);
//			double lng_3 = lng_2;
//			
//			double lat_4 = lat_center - ((0.5 - ssize) * (max_lat - min_lat));
//			double lng_4 = lng_center;
//			
//			double lat_5 = lat_3;
//			double lng_5 = lng_center + ((lat_1 - lat_center) / 2 * Math.sqrt(3));
//			
//			double lat_6 = lat_2;
//			double lng_6 = lng_5;
			
			
			
			double lat_1 = lat_center + ((0.5 - ssize) * (max_lat - min_lat));
			double lng_1 = lng_center;
			
			double lat_2 = lat_1 - ((lat_1 - lat_center) / 2);
			double lng_2 = lng_center - ((max_lng - min_lng) * (0.5 - ssize));
			
			double lat_3 = lat_center - ((lat_1 - lat_center) / 2);
			double lng_3 = lng_2;
			
			double lat_4 = lat_center - ((0.5 - ssize) * (max_lat - min_lat));
			double lng_4 = lng_center;
			
			double lat_5 = lat_3;
			double lng_5 = lng_center + ((max_lng - min_lng) * (0.5 - ssize));
			
			double lat_6 = lat_2;
			double lng_6 = lng_5;
			
			
			byte[][] grid = new byte[13][];
			
			grid[0] = ABUtils.intToBytes2((int)(lng_1 * 100000));
			grid[1] = ABUtils.intToBytes2((int)(lat_1 * 100000));
			
			grid[2] = ABUtils.intToBytes2((int)(lng_2 * 100000));
			grid[3] = ABUtils.intToBytes2((int)(lat_2 * 100000));
			
			grid[4] = ABUtils.intToBytes2((int)(lng_3 * 100000));
			grid[5] = ABUtils.intToBytes2((int)(lat_3 * 100000));
			
			grid[6] = ABUtils.intToBytes2((int)(lng_4 * 100000));
			grid[7] = ABUtils.intToBytes2((int)(lat_4 * 100000));
			
			grid[8] = ABUtils.intToBytes2((int)(lng_5 * 100000));
			grid[9] = ABUtils.intToBytes2((int)(lat_5 * 100000));
			
			grid[10] = ABUtils.intToBytes2((int)(lng_6 * 100000));
			grid[11] = ABUtils.intToBytes2((int)(lat_6 * 100000));
			
			grid[12] = ABUtils.intToBytes2(count);
			
			return grid;
		}
	}
	
	

	/*
	 * type:0 grid,grid_num 1 hexgrid
	 */
	private static byte[] splits(List<Map<String,Object>> points, int uk,int type) throws IOException {
		double min_lng = 180, max_lng = 0, min_lat = 90, max_lat = 0;

		for (Map<String,Object> m : points) {
			double lng = (double)m.get("lng");
			double lat = (double)m.get("lat");
			if (lng < min_lng)
				min_lng = lng;
			if (lng > max_lng)
				max_lng = lng;
			if (lat < min_lat)
				min_lat = lat;
			if (lat > max_lat)
				max_lat = lat;
		}

		int min_x = (int) MercatorProjection.longitudeToMetersX(min_lng);
		int max_x = (int) MercatorProjection.longitudeToMetersX(max_lng);

		int min_y = (int) MercatorProjection.latitudeToMetersY(min_lat);
		int max_y = (int) MercatorProjection.latitudeToMetersY(max_lat);

		int mod1 = (max_x - min_x) % (uk * 1000);

		if (mod1 != 0) {
			max_x += uk * 1000 - mod1;
			max_lng = MercatorProjection.metersXToLongitude(max_x);
		}

		int split_x = (max_x - min_x) / uk / 1000;

		double unit_x = (max_lng - min_lng) / split_x;

		mod1 = (max_y - min_y) % (uk * 1000);

		if (mod1 != 0) {
			max_y += uk * 1000 - mod1;
			max_lat = MercatorProjection.metersYToLatitude(max_y);
		}

		int split_y = (max_y - min_y) / uk / 1000;

		double unit_y = (max_lat - min_lat) / split_y;

		Map<String, Integer> map = new HashMap<String, Integer>();

		for (Map<String,Object> m : points) {
			double lng = (double)m.get("lng");
			double lat = (double)m.get("lat");
			int seq_x = (int) ((lng - min_lng) / unit_x);
			int seq_y = (int) ((lat - min_lat) / unit_y);

			String key = seq_x+"-"+seq_y;

			if (map.containsKey(key)) {
				map.put(key, map.get(key) + 1);
			} else {
				map.put(key, 1);
			}
		}

//		StringBuilder sb = new StringBuilder(
//				"{\"type\":\"FeatureCollection\",\"features\":[");
//
//		boolean firstLine = true;
		
		ByteArrayOutputStream abo = new ByteArrayOutputStream();

		for (Map.Entry<String, Integer> en : map.entrySet()) {
			String[] key = en.getKey().split("-");
			int sx = Integer.parseInt(key[0]);
			int sy = Integer.parseInt(key[1]);
			double i_lng = min_lng + (sx * unit_x);
			double i_lat = min_lat + (sy * unit_y);
			
			double x_lng = i_lng + unit_x;
			double x_lat = i_lat + unit_y;
			
			Grid grid = new Grid(i_lng,i_lat,x_lng,x_lat,en.getValue());
			
			if (type == 1){
				byte[][] gv = grid.getGridhex();
				for(byte[] bs: gv)
				 abo.write(bs);
			}else{
				byte[][] gv = grid.getGrid();
				for(byte[] bs: gv)
				 abo.write(bs);
			}

//			if (firstLine) {
//				firstLine = false;
//			} else {
//				sb.append(",");
//			}
//
//			sb.append("{\"type\":\"Feature\",\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[");
//			
//			sb.append("["+lng+","+lat+"]");
//			sb.append(",["+(lng + unit_x)+","+lat+"]");
//			sb.append(",["+(lng + unit_x)+","+(lat + unit_y)+"]");
//			sb.append(",["+lng+","+(lat + unit_y)+"]");
//			sb.append(",["+lng+","+lat+"]");
//			
//			sb.append("]]},\"properties\":{\"count\":"+en.getValue()+"}}");
		}

//		sb.append("]}");
//		
//		return sb.toString();
		
		return abo.toByteArray();

	}
	
	
	public static byte[] getGrids(JdbcTemplate jdbc, String sql,int km,int type) throws IOException {
		
		
		List<Map<String,Object>> ps = jdbc.queryForList(sql);
		
		 
		byte[] value = GridHeatmapUtils.splits(ps,km,type);
		
		return value;
		
	}

}
